<template>
  <ul class="ul" id="ul" @dragstart="dragStart" @dragover="dragOver" @dragenter="dragEnter" @dragend="dragEnd">
    <li draggable="true" v-for="item in list" :key="item.id" :data-id="item.id" class="li">
      <slot name="item" :item="item">{{ item }}</slot>
    </li>
  </ul>
</template>
<script lang="ts">
import { defineComponent, reactive, toRefs, ref } from 'vue'

export default defineComponent({
  name: 'DrageItem',
  emits: ['dragEnd'],
  components: {},
  props: {
    list: {
      type: Array as any,
      default: () => []
    }
  },
  setup(props, { emit }) {
    const components = {
      ulRef: ref(null)
    }
    const state = reactive({
      list: props.list,
      dragging: '' as unknown as Element,
      targetItem: null,
      oldIndex: '' as any
    })
    const methods = {
      dragStart(e: any) {
        state.dragging = e.target
        setTimeout(() => {
          e.target.classList.add('dragging')
        }, 0)
        const ulRef = document.querySelector('#ul') || {} as Element
        const liArr = Array.from(ulRef.children)
        state.oldIndex = liArr.indexOf(e.target)
        // console.log(state.oldIndex);

      },
      dragOver(e: MouseEvent) {
        e.preventDefault()
      },
      dragEnter(e: MouseEvent) {
        e.preventDefault()
        const ulRef = document.querySelector('#ul') || {} as Element
        const liArr = Array.from(ulRef.children)
        const targetIndex = liArr.indexOf(e.target as Element)
        const sourceIndex = liArr.indexOf(state.dragging)
        if (e.target === state.dragging.parentNode || e.target === state.dragging) return
        if (targetIndex > sourceIndex) {
          ulRef?.insertBefore(state.dragging, (e.target as Element).nextSibling)
        } else {
          ulRef?.insertBefore(state.dragging, e.target as Element)
        }
      },
      async dragEnd() {
        const ulRef = document.querySelector('#ul') || {} as Element
        const liArr = Array.from(ulRef.children)
        const newIndex = liArr.indexOf(state.dragging)
        try {
          if (newIndex === state.oldIndex) return
          state.list = await methods.sortList(state.list, state.oldIndex, newIndex)
          emit('dragEnd', state.list)
        } catch (err: any) {
          throw new Error(err)
        }
        finally {
          // console.log(state.list)
          state.dragging.classList.remove('dragging')
        }

      },

      async sortList(list: any, oldIndex: number, newIndex: number) {
        if (!list || typeof list !== 'object') {
          throw new Error('请确保第一个参数list是一个数组')
        }
        const newItem = list[oldIndex]
        if (newIndex === oldIndex) return list
        if (newIndex > oldIndex) {
          for (let i = oldIndex; i <= newIndex; i++) {
            if (i === newIndex) {
              list[i] = newItem
              break
            }
            list[i] = list[i + 1]

          }

        } else {
          for (let i = oldIndex; i >= newIndex; i--) {
            if (i === newIndex) {
              list[i] = newItem
              break
            }
            list[i] = list[i - 1]

          }

        }
        return list
      }
    }

    return {
      ...toRefs(state),
      ...methods
    }
  }
})
</script>
<style lang="scss" scoped>
.ul {
  padding: 0;
  margin: 0;
  margin: 50px 100px;
  padding-bottom: 100px;
  list-style: none;
  transition: all 0.5s;
  display: flex;
  flex-direction: column;

  .li {
    padding: 10px;
    background-color: #e9f1fe;
    margin: 5px 0;
    cursor: pointer;
    transition: all 0.5s;
  }
}

.dragging {
  opacity: 0.4;
  transition: all 0.4s;
}
</style>
